﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.IO.Ports;

using canlibCLSNET;

namespace CANMonitor
{
    public partial class Form1 : Form
    {
		private List<GroupBox> Get_GroupBox_List()
		{
			List<GroupBox> ret = new List<GroupBox>();
			//遍历Form上的所有控件
			foreach (System.Windows.Forms.Control control in this.Controls)
			{
				if (control is System.Windows.Forms.GroupBox)
				{
					ret.Add(control as GroupBox);
				}
			}
			return ret;
		}

		private List<RadioButton> Get_Radio_List(GroupBox box)
		{
			List<RadioButton> ret = new List<RadioButton>();
			//遍历groupBox1上的所有控件
			foreach (System.Windows.Forms.Control control in box.Controls)
			{
				if (control is System.Windows.Forms.RadioButton)
				{
					ret.Add(control as RadioButton);
				}
			}
			return ret;
		}

		private List<GroupBox> m_signal_box_list = new List<GroupBox>();

		private void Update_GroupBox(GroupBox box, CanMsg msg)
		{
			CanSignal signal = box.Tag as CanSignal;
			if (msg.mID != signal.id) return;

			int value = signal.GetValue(msg);
			List<RadioButton> list = Get_Radio_List(box);
			RadioButton default_radio = new RadioButton();
			foreach (RadioButton item in list)
			{
				if (item.Text.StartsWith("-")) { default_radio = item; continue; }
				if (item.Tag == null)
				{
					string[] str = item.Text.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
					item.Tag = int.Parse(str[0]);
				}
				int tag_value = (int)item.Tag;
				item.Checked = (tag_value == value) ? true : false;
				if (item.Checked == true) return;
			}

			default_radio.Checked = true;
		}

		private void Update_AllGroupBox(CanMsg msg)
		{
			foreach (GroupBox item in m_signal_box_list)
			{
				Update_GroupBox(item, msg);
			}
		}
        public Form1()
        {

			InitializeComponent();
			Canlib.canInitializeLibrary();

            Timer_init();
            txt_log.Clear();
			Log("APP Start");
			comb_can_Click(null, null);
			comb_can.Focus();

			List<GroupBox> list = Get_GroupBox_List();
			foreach(GroupBox item in list)
			{
				if (item.Tag is string)
				{
					string tag = item.Tag as string;
					string[] str = tag.Split(new char[]{' ',','},StringSplitOptions.RemoveEmptyEntries);
					try	{
						UInt32 id = UInt32.Parse(str[0], System.Globalization.NumberStyles.HexNumber);
						int byte_no = int.Parse(str[1]);
						int start_bit = int.Parse(str[2]);
						int end_bit = int.Parse(str[3]);
						item.Tag = new CanSignal(id, byte_no, start_bit, end_bit);
						m_signal_box_list.Add(item as GroupBox);
					}
					catch{
						continue;
					}
				}
			}  

			////for test
			//int value = 11;
			//CanMsg msg1 = CanMsg.Parse("4.004150 254 rx std d 8 00 00 00 00 00 00 00 00");
			//CanSignal.SetValue(msg1, "254 0 5 3", value); //识别结果
			//CanSignal.SetValue(msg1, "254 0 2 0", value); //正在执行
			//CanSignal.SetValue(msg1, "254 1 7 4", value); //录入/识别 过程提示
			//Update_AllGroupBox(msg1);

			//CanMsg msg2 = CanMsg.Parse("4.004150 450 rx std d 8 00 00 00 00 00 00 00 00");
			//CanSignal.SetValue(msg2, "450 0 7 6", value); //驾驶员目光
			//CanSignal.SetValue(msg2, "450 0 5 2", value); //驾驶员目光
			//CanSignal.SetValue(msg2, "450 1 7 6", value); //驾驶员目光
			//CanSignal.SetValue(msg2, "450 1 5 3", value); //驾驶员目光
			//CanSignal.SetValue(msg2, "450 1 2 0", value); //驾驶员目光
			//CanSignal.SetValue(msg2, "450 2 7 4", value); //驾驶员目光
			//CanSignal.SetValue(msg2, "450 2 3 0", value); //驾驶员目光
			//Update_AllGroupBox(msg2);
        }

		private void DisplayError(Canlib.canStatus status, String routineName)
		{
			String errText = "";
			if (status != Canlib.canStatus.canOK)
			{
				Canlib.canGetErrorText(status, out errText);
				errText += ".\nError code = " + status.ToString() + ".";
				MessageBox.Show(errText, routineName, MessageBoxButtons.OK);
			}
		}

        //-----------------------------------------------------------------------------------
        // Log相关
		int log_cnt;
        private void Log(string log)
        {
            string time_stamp = MillisecondTimer.GetDateTime().ToString("[hh:mm:ss.fff] ");
            txt_log.AppendText(time_stamp + log + "\r\n");

			if ((log_cnt++) > 1000)
			{
				txt_log.Clear();
				log_cnt = 0;
			}
        }

		private void Log(CanMsg msg)
		{
			Log(msg.ToShortString());

			string head = msg.mIsTx ? "发出" :"收到";
			switch (msg.mID)
			{
				case 0x253: //显示发出的控制信号
					string[] req_str = new string[] { "Disable", "Recognize", "Enroll", "ClearAll", "DeleteMid", "KeepAwake", "NotUsed_1", "Faulty", };
					Log(head + "命令：" + req_str[msg.mData[0] >> 5]);
					break;

				case 0x254: //显示收到的状态信号
					string[] DrvIdMde_D_Stat = new string[] { "00 Null", "01 Recognize", "02 Enroll", "03 ClearAll", "04 DeleteMid", "05 DBCorrupt", "06 NotUsed_2", "07 NotUsed_3" };
					string[] DrvExpression_D_Stat = new string[]{"00 Null","01 FaceNotInView","02 TooManyFaces","03 OutsidePoseLimits","04 MouthOpen","05 FaceBlockage"
																	,"06 EyesNotVisible","07 PoorTrackingQuality","08 NotAFace","09 ConstraintsOk","0A HasRegister"
																	,"0B FailNoSlot","0C FailMultipleMatches","0D NotUsed_2","0E NotUsed_3","0F NotUsed_4"};
					string[] DrvId_D_Stat = new string[] { "00 Null", "01 OkMatched", "02 OkNoMatch", "03 FailTimeout", "04 NotUsed_1", "05 NotUsed_2", "06 NotUsed_3", "07 NotUsed_4" };

					int drv_mode = (msg.mData[0] >> 0) & 0x7; //byte0.b2..0
					Log(head+"状态：" + "DriverId_Mode_Status = " + DrvIdMde_D_Stat[drv_mode]);

					int drv_id = (msg.mData[0] >> 3) & 0x7;//byte0.b5..3
					Log(head+"状态：DriverId_Recognition_Result = " + DrvId_D_Stat[drv_id]);

					int dev_express = (msg.mData[1] >> 4) & 0xF;//byte1.b7..4
					Log(head+"状态：PoseExpression_Status = " + DrvExpression_D_Stat[dev_express]);
					break;
				default:

					break;
			}
		}

		//-----------------------------------------------------------------------------------
		// CAN相关

		private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
		{
			int seg1 = int.Parse(txt_aseg1.Text);
			int seg2 = 79 - seg1;
			double percent = (1 + seg1); percent /= (1 + seg1 + seg2); percent *= 100;
			txt_aseg2.Text = seg2.ToString();
			txt_abaud.Text = "500kbps(" + percent.ToString("F1") + "%)";
		}

		private void txt_dseg1_SelectedIndexChanged(object sender, EventArgs e)
		{
			int seg1 = int.Parse(txt_dseg1.Text);
			int seg2 = 19 - seg1;
			double percent = (1 + seg1); percent /= (1 + seg1 + seg2); percent *= 100;
			txt_dseg2.Text = seg2.ToString();
			txt_dbaud.Text = "2000Kbps(" + percent.ToString("F1") + "%)";
		}

		private int Device_Open(int channel)
		{
			Canlib.canStatus status;

			int canHandle = -1;
			if (ckbox_iso.Checked == true)  canHandle = Canlib.canOpenChannel(channel, Canlib.canOPEN_CAN_FD | Canlib.canOPEN_EXCLUSIVE | Canlib.canOPEN_ACCEPT_VIRTUAL);
			if (ckbox_iso.Checked == false) canHandle = Canlib.canOpenChannel(channel, Canlib.canOPEN_CAN_FD_NONISO | Canlib.canOPEN_EXCLUSIVE | Canlib.canOPEN_ACCEPT_VIRTUAL);
			if (canHandle < 0)	return canHandle;

			int aseg1 = int.Parse(txt_aseg1.Text); int aseg2 = int.Parse(txt_aseg2.Text); int asjw = int.Parse(txt_asjw.Text);
			status = Canlib.canSetBusParams(canHandle, 500000, aseg1, aseg2, asjw, 3, 0);
			if (status != Canlib.canStatus.canOK) return canHandle;

			int dseg1 = int.Parse(txt_dseg1.Text); int dseg2 = int.Parse(txt_dseg2.Text); int dsjw = int.Parse(txt_dsjw.Text);
			status = Canlib.canSetBusParamsFd(canHandle, 2000000, dseg1, dseg2, dsjw);
			if (status != Canlib.canStatus.canOK) return canHandle;

			// The canSetNotify() function associates a window handle with the CAN circuit.
			// A WM__CANLIB message is sent to that window when certain events (specified by the canNOTIFY_xxx flags) occur.
			// In this case we will get a CAN message reception notification.
			status = Canlib.canSetNotify(canHandle, Handle, Canlib.canNOTIFY_RX);
			if (status != Canlib.canStatus.canOK) return canHandle;

			status = Canlib.canBusOn(canHandle);
			if (status != Canlib.canStatus.canOK) return canHandle;
			return canHandle;
		}

		private void Device_Close(int handle)
		{
			Canlib.canStatus status;
			status = Canlib.canSetNotify(handle, (System.IntPtr)null, Canlib.canNOTIFY_RX);
			status = Canlib.canClose(handle);
		}

		private List<CanMsg> Device_Read(int handle)
		{
			List<CanMsg> ret = new List<CanMsg>();
			do
			{
				long time; int id, dlc, flag; byte[] data = new byte[64];
				Canlib.canStatus status = Canlib.canRead(handle, out id, data, out dlc, out flag, out time);
				if (status == Canlib.canStatus.canOK)
				{
					byte[] data_lite = new byte[dlc]; for (int i = 0; i < dlc; i++) data_lite[i] = data[i];
					CanMsg item = CanMsg.Parse((uint)time, (uint)id, data_lite, false, (flag & Canlib.canMSG_EXT) != 0, (flag & Canlib.canMSG_RTR) != 0);
					ret.Add(item);
				}
				else
					break;
			} while (true);
			return ret;
		}

		private object mlock_write = new object();
		private bool Device_Write(int handle, CanMsg msg)
		{
			lock (mlock_write)
			{
				if (m_device_handle < 0) return false;

				int id = (int)msg.mID;
				int dlc = msg.mData.Length;
				int flag = 0;
				if (msg.mIsExt) flag |= Canlib.canMSG_EXT;
				if (msg.mIsRtr) flag |= Canlib.canMSG_RTR;
				byte[] data = msg.mData;
				Canlib.canStatus status = Canlib.canWrite(handle, id, data, dlc, flag);
				if (status == Canlib.canStatus.canOK)
				{
					status = Canlib.canWriteSync(handle, 50);
				}
				if (status == Canlib.canStatus.canOK)
				{
					return true;
				}
				return false;
			}
		}

		private void comb_can_Click(object sender, EventArgs e)
		{
			string[] port_list = CanAlyst_Kvaser.GetPortNames();
            comb_can.Items.Clear();
            comb_can.Items.AddRange(port_list);
			if (port_list.Length > 0)
			{
				if (comb_can.SelectedIndex < 0 || comb_can.SelectedIndex >= port_list.Length)
					comb_can.SelectedIndex = 0;
			}
		}

		int m_device_handle;
		private void but_Can_Start_Click(object sender, EventArgs e)
		{
			Canlib.canStatus status;
			try //操作发送can， 关心是否操作失败
			{
				if (but_can.Text == "打开CAN")
				{

					m_device_handle = Device_Open(comb_can.SelectedIndex);
					if (m_device_handle < 0) throw(new Exception("Open Failed!"));

					comb_can.Enabled = false;
					txt_aseg1.Enabled = false; txt_asjw.Enabled = false;
					txt_dseg1.Enabled = false; txt_dsjw.Enabled = false;
					but_can.Text = "关闭CAN";	but_can.BackColor = Color.LightGreen;
				}
				else
				{
					status = Canlib.canSetNotify(m_device_handle, (System.IntPtr)null, Canlib.canNOTIFY_RX);
					status = Canlib.canClose(m_device_handle);
					Device_Close(m_device_handle);
					m_device_handle = -1;

					comb_can.Enabled = true; 
					txt_aseg1.Enabled = true; txt_asjw.Enabled = true;
					txt_dseg1.Enabled = true; txt_dsjw.Enabled = true; 
					but_can.Text = "打开CAN"; but_can.BackColor = Color.Transparent;
				}
			}
			catch
			{
				MessageBox.Show("操作CAN盒" + comb_can.Text + "失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		List<CanMsg> m_rx_list = new List<CanMsg>();
		protected override void WndProc(ref Message m)
		{
			Canlib.canStatus status;
			if (m.Msg == Canlib.WM__CANLIB)
			{
				try
				{
					List<CanMsg> list = Device_Read(m_device_handle);
					lock (m_rx_list)
					{
						m_rx_list.AddRange(list);
					}
				}
				catch
				{
					// an error communicating with the hardware detected so shutdown
					status = Canlib.canSetNotify(m_device_handle, (System.IntPtr)null, Canlib.canNOTIFY_RX);
					DisplayError(status, "unregister canSetNotify");

					Canlib.canBusOff(m_device_handle);
					Canlib.canClose(m_device_handle);

					comb_can.Enabled = true; but_can.Text = "打开CAN";
					but_can.BackColor = Color.Transparent;
				}
			}

			base.WndProc(ref m);
		}

		public List<CanMsg> GetCanMsg()
		{
			List<CanMsg> list = new List<CanMsg>();
			lock (m_rx_list)
			{
				list.AddRange(m_rx_list);
				m_rx_list.Clear();
			}
			return list;
		}

		public bool SendCanMsg(CanMsg msg)
		{
			return Device_Write(m_device_handle, msg);
		}

        //-----------------------------------------------------------------------------------
		//-----------------------------------------------------------------------------------
        // CAN报文 收发

		CanMsg m_can_msg = CanMsg.Parse(0, 0x253, new byte[8], true, false, false);

        private void but_signal_Click(object sender, EventArgs e)
        {
			Button button = sender as Button;
			string[] substr = button.Text.Split(new char[] { ' ' });
			uint req = 0;
			if (uint.TryParse(substr[0], out req) == true)
			{
				//发出一帧报文
				m_can_msg.mData[0] = (byte)(req << 5);
				bool ret = SendCanMsg(m_can_msg);
				if (ret == true)
					Log(m_can_msg);
				else
					Log("发送报文失败！！！！");
			}
        }
		private void Can_Read_Service()
		{
			List<CanMsg> list = GetCanMsg(); //读取收到的can报文
			foreach (CanMsg item in list)    //显示can报文
			{
				Log(item);

				if (item.mID == 0x253 || item.mID == 0x254 || item.mID == 0x450)
				{
					Update_AllGroupBox(item);
				}
			}
		}

        //-----------------------------------------------------------------------------------
        //处理收到的消息
        Timer m_10mstimer = null;
		MillisecondTimer m_ticks = new MillisecondTimer();

        private void Timer_init()
        {
            if (m_10mstimer == null)
            {
                m_10mstimer = new Timer();
                m_10mstimer.Interval = 100;
                m_10mstimer.Tick += new EventHandler(m_10mstimer_Tick);
            }
            m_10mstimer.Start();

			m_ticks = new MillisecondTimer();
			m_ticks.Interval = 1;
			m_ticks.Tick += new EventHandler(m_1msticks_Tick);
			m_ticks.Start();
        }



        void m_10mstimer_Tick(object sender, EventArgs e)
        {
			//接收CAN
			Can_Read_Service();

			//显示诊断信息
        }

		//-----------------------------------------------------------------------
		//周期发送函数

		bool mMsg3B3_enable = false;
		bool mMsg59E_enable = false;
		CanMsg mMsg3B3_500ms  = CanMsg.Parse(0, 0x3B3, new byte[8]{0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, true, false, false);
		CanMsg mMsg59E_1000ms = CanMsg.Parse(0, 0x59E, new byte[8]{0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00 }, true, false, false);

		UInt64 m_systick = 0;
		void m_1msticks_Tick(object sender, EventArgs e)
		{
			m_systick++;

			if ((m_systick % 500) == 499) //500ms
			{
				if (mMsg3B3_enable) SendCanMsg(mMsg3B3_500ms);
			}
			if ((m_systick % 1000) == 699) //1000ms
			{
				if (mMsg59E_enable) SendCanMsg(mMsg59E_1000ms);
			}
		}

		private void but_Msg3B3_Click(object sender, EventArgs e)
		{
			if (but_Msg3B3.BackColor == Color.Transparent)
			{ but_Msg3B3.BackColor = Color.LightGreen; mMsg3B3_enable = true; }
			else
			{ but_Msg3B3.BackColor = Color.Transparent; mMsg3B3_enable = false; }
		}

		private void but_Msg59E_Click(object sender, EventArgs e)
		{
			if (but_Msg59E.BackColor == Color.Transparent)
			{ but_Msg59E.BackColor = Color.LightGreen; mMsg59E_enable = true; }
			else
			{ but_Msg59E.BackColor = Color.Transparent; mMsg59E_enable = false; }
		}
		//-----------------------------------------------------------------------

	}


	public class CanSignal
	{
		public UInt32 id;
		public int byte_no;
		public int start_bit;
		public int stop_bit;

		public CanSignal(UInt32 _id, int _byte_no, int _start_bit, int stop_bit)
		{
			this.id = _id; this.byte_no = _byte_no; this.start_bit = _start_bit; this.stop_bit = stop_bit;
		}

		public int GetValue(CanMsg msg)
		{
			if (msg.mID != this.id)
				throw new Exception("ID不一致，无法获取信号值！");

			byte value = msg.mData[byte_no];
			value <<= (7 - this.start_bit);
			value >>= (7 - this.start_bit);
			value >>= this.stop_bit;

			return (int)value;
		}

		public void SetValue(CanMsg msg, int value)
		{
			if (msg.mID != this.id)
				throw new Exception("ID不一致，无法设置信号值！");

			byte mask = 0xFF;
			mask <<= (7 - this.start_bit);
			mask >>= (7 - this.start_bit);
			mask >>= this.stop_bit;

			byte bval = (byte)value;
			bval <<= this.stop_bit;
			bval <<= (7 - this.start_bit);
			bval >>= (7 - this.start_bit);

			msg.mData[this.byte_no] &= (byte)(~mask);
			msg.mData[this.byte_no] |= (byte)bval;

		}

		public static CanSignal Parse(string str) // "id byte start_bit stop_bit
		{
			string[] arr = str.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
			try
			{
				UInt32 id = UInt32.Parse(arr[0], System.Globalization.NumberStyles.HexNumber);
				int byte_no = int.Parse(arr[1]);
				int bit_start = int.Parse(arr[2]);
				int bit_stop = int.Parse(arr[3]);

				return new CanSignal(id, byte_no, bit_start, bit_stop);
			}
			catch
			{
				return null;
			}
		}

		public static void SetValue(CanMsg msg, string tag, int value)
		{
			CanSignal sig = CanSignal.Parse(tag);
			sig.SetValue(msg, value);
		}
	}
}
